#include "MessageParser.h"
#include "Messaging.h"
#include <QStringList>
#include <ParametersBean.h>

using namespace Networking::Messaging;
using Networking::MessageParser;

Command MessageParser::ParseMessage(const QString& msg, int senderID) const
{
    if(msg.startsWith("SC_"))
    {
        return parseServerCommand(msg);
    }
    else if(msg.startsWith("CS_"))
    {
        return parseClientRequest(msg, senderID);
    }
    else
        return Command();
}

QString MessageParser::removeEOM(const QString& msg) const
{
    return msg.left(msg.size() - EOMSequence.size());
}

Command MessageParser::parseClientRequest(const QString& msg, int senderID) const
{
    Command res;
    if(!msg.endsWith(EOMSequence))
    {
        return res;
    }

    QStringList msgParts = removeEOM(msg).split(DelimeterSequence, QString::SkipEmptyParts);
    const QString msgHeader = msgParts.first();

    // "CS_SEND_NICK"
    if(MessageHeaders[Command::eReqSendNickname] == msgHeader)
    {
        if(2 == msgParts.size())
        {
            QString message = msgParts.last();
            res = Command(Command::eReqSendNickname, new MessageBean(message));
        }
    }
    // "CS_DISCONNECT"
    else if(MessageHeaders[Command::eReqDisconnect] == msgHeader)
    {
        res.SetType(Command::eReqDisconnect);
    }
    // "CS_MOVE"
    else if(MessageHeaders[Command::eReqMove] == msgHeader)
    {
        res.SetType(Command::eReqMove);
    }
    // "CS_FIRE"
    else if(MessageHeaders[Command::eReqFire] == msgHeader)
    {
        res.SetType(Command::eReqFire);
    }
    // "CS_TURN"
    else if(MessageHeaders[Command::eReqTurn] == msgHeader)
    {
        if(2 == msgParts.size())
        {
            QString direction = msgParts.last();
            if(direction == TurnDirections[Tank::eTurnLeft])
            {
                res= Command(Command::eReqTurnLeft);
            }
            else if(direction == TurnDirections[Tank::eTurnRight])
            {
                res= Command(Command::eReqTurnRight);
            }
        }
    }
    // "CS_STOP_MOVE"
    else if(MessageHeaders[Command::eReqStopMove] == msgHeader)
    {
        res.SetType(Command::eReqStopMove);
    }
    // "CS_STOP_FIRE"
    else if(MessageHeaders[Command::eReqStopFire] == msgHeader)
    {
        res.SetType(Command::eReqStopFire);
    }
    return res;
}


Command MessageParser::parseServerCommand(const QString& msg) const
{
    const int actCmdSize = 2;
    Command res;
    if(!msg.endsWith(EOMSequence))
        return res;

    QStringList msgParts = removeEOM(msg).split(DelimeterSequence, QString::SkipEmptyParts);
    const QString msgHeader = msgParts.first();
    //unfinished: add Data for each command

    // "SC_CONN_ACCEPTED"
    if(MessageHeaders[Command::eActConnAccepted] == msgHeader)
    {
        res = createRequest(Command::eActConnAccepted, msgParts);
    }
    // "SC_CONN_REJECTED"
    else if(MessageHeaders[Command::eActConnRejected] == msgHeader)
    {
        if(2 == msgParts.size())
        {
            res = Command(Command::eActConnRejected, new MessageBean(msgParts.last()));
        }
    }
    // "SC_PLAYERLIST"
    else if(MessageHeaders[Command::eActPlayerlist] == msgHeader)
    {
        // if we have proper number of message parameters
        if(0 == (msgParts.size() - 1) % 2)
        {
            PlayerlistBean::Playerlist playerlist;
            QStringList::iterator end = msgParts.end();
            for(QStringList::iterator iter = msgParts.begin(); iter != end; ++iter)
            {
                //get next player
                Player entry(iter->toUInt(), *(++iter));
                //add it to result playerlist
                playerlist.push_back(entry);
            }
            res = Command(Command::eActPlayerlist, new PlayerlistBean(playerlist));
        }
    }
    // "SC_GAME_START"
    else if(MessageHeaders[Command::eActGameStart] == msgHeader)
    {
        res.SetType(Command::eActGameStart);
    }
    // "SC_GAME_END"
    else if(MessageHeaders[Command::eActGameEnd] == msgHeader)
    {
        res = createRequest(Command::eActGameEnd, msgParts);
    }
    // "SC_SEND_SNAPSHOT"
    else if(MessageHeaders[Command::eActSendSnapshot] == msgHeader)
    {
        const int tanksCnt = msgParts.at(1).toInt();

        const int tankEntrySize = 7;
        const int bulletEntrySize = 5;
        const int lastTankEntry = tanksCnt*tankEntrySize + 1;

        Tanks tanks = getTanks(msgParts, tanksCnt, tankEntrySize);
        Bullets bullets = getBullets(msgParts, lastTankEntry, bulletEntrySize);
        res=Command(Command::eActSendSnapshot, new SnapshotBean(tanks, bullets));

    }
    // "SC_SEND_MAP"
    else if(MessageHeaders[Command::eActSendMap] == msgHeader)
    {
        const int mapSizeIndex = 1;
        const int dataIndex = 2;
        const int healthIndex = 3;
        const int speedIndex = 4;
        const int bulletSpeedIndex = 5;
        const int firerateIndex = 6;

        if(7 == msgParts.size())
        {
            QStringList::iterator iter = msgParts.begin() + 1;
            unsigned size = msgParts[mapSizeIndex].toUInt();
            QString data = msgParts[dataIndex];
            unsigned health = msgParts[healthIndex].toUInt();
            unsigned speed = msgParts[speedIndex].toUInt();
            unsigned bulletSpeed = msgParts[bulletSpeedIndex].toUInt();
            unsigned firerate = msgParts[firerateIndex].toUInt();

            GameSettings sets(speed, firerate, health, bulletSpeed);
            MapPtr map(new Map(size, data));
            res = Command(Command::eActSendMap, new MapBean(map, sets));
        }
    }
    // "SC_MOVE"
    else if(MessageHeaders[Command::eActMove] == msgHeader)
    {
        res = createRequest(Command::eActMove, msgParts);
    }
    // "SC_FIRE"
    else if(MessageHeaders[Command::eActFire] == msgHeader)
    {
        res = createRequest(Command::eActFire, msgParts);
    }
    // "SC_TURN"
    else if(MessageHeaders[Command::eActTurn] == msgHeader)
    {
        //unfinished
        if(actCmdSize + 1 == msgParts.size())
        {
            QString direction = msgParts.last();
            if(direction == TurnDirections[Tank::eTurnLeft])
            {
               // res= Command(Command::eReqTurn, new TurnBean(1, Tank::eTurnLeft));
            }
            else if(direction == TurnDirections[Tank::eTurnRight])
            {
               // res= Command(Command::eReqTurn, new TurnBean(1, Tank::eTurnRight));
            }
        }
    }
    // "SC_STOP_MOVE"
    else if(MessageHeaders[Command::eActStopMove] == msgHeader)
    {
        res = createRequest(Command::eActStopMove, msgParts);
    }
    // "SC_STOP_FIRE";
    else if(MessageHeaders[Command::eActStopFire] == msgHeader)
    {
            res = createRequest(Command::eActStopFire, msgParts);
    }
    return res;
}

Command MessageParser::createRequest(Command::CommandType cmd, const QStringList& parts) const
{
    // const int actCmdSize = 2;
    if(2 == parts.size())
    {
        return Command(cmd, new PlayerIDBean(parts.last().toInt()));
    }
    return Command();
}


Tank::CardinalDirection MessageParser::parseDirection(const QString& dir) const
{
                if(ActionTypes[Tank::eNorth] == dir)
                {
                  return Tank::eNorth;
                }
                else if(ActionTypes[Tank::eSouth] == dir)
                {
                   return Tank::eSouth;
                }
                else if(ActionTypes[Tank::eEast] == dir)
                {
                   return Tank::eEast;
                }
                else
                    //if(ActionTypes[Tank::eWest] == dir)
                {
                   return Tank::eWest;
                }
}


Tank::ActionType MessageParser::parseActionType(const QString& dir) const
{
            if(ActionTypes[Tank::eMoving] == dir)
            {
               return Tank::eMoving;
            }
            else if(ActionTypes[Tank::eTurningRight] == dir)
            {
               return Tank::eTurningRight;
            }
            else if(ActionTypes[Tank::eTurningLeft] == dir)
            {
               return Tank::eTurningLeft;
            }
            else
            //if(ActionTypes[Tank::eStaying] == dir)
            {
              return Tank::eStaying;
            }
}

Tanks MessageParser::getTanks(const QStringList& msgParts, int tanksCnt, int tankEntrySize) const
{
    //for tanks
    const int idIndex = 2;
    const int healthIndex = 3;
    const int posXIndex = 4;
    const int posYIndex = 5;
    const int actionIndex = 6;
    const int directionIndex = 7;
    const int progressIndex = 8;

    Tanks tanks;
    for(int i = 0; i < tanksCnt; ++i)
    {
        unsigned id = msgParts.at(tankEntrySize * i + idIndex).toInt();
        unsigned health =  msgParts.at(tankEntrySize * i + healthIndex).toInt();
        unsigned posX =  msgParts.at(tankEntrySize * i + posXIndex).toInt();
        unsigned posY = msgParts.at(tankEntrySize * i + posYIndex).toInt();
        Tank::ActionType action = parseActionType(msgParts.at(tankEntrySize * i + actionIndex));
        DynamicObject::CardinalDirection direction = parseDirection(msgParts.at(tankEntrySize * i + directionIndex));
        unsigned progress = msgParts.at(tankEntrySize * i + progressIndex).toUInt();
        TankPtr curr(new Tank(id, posX, posY, direction, action, progress));
        tanks.push_back(curr);
    }
    return tanks;
}

Bullets MessageParser::getBullets(const QStringList& msgParts, int lastTankEntry, int bulletEntrySize) const
{
    //for tanks
    const int idIndex = 0;
    const int posXIndex = 1;
    const int posYIndex = 2;
    const int directionIndex = 3;
    const int progressIndex = 4;

    const int bulletsCnt = (msgParts.size() - lastTankEntry) / bulletEntrySize;
    Bullets bullets;
    for(int i = lastTankEntry + 1; i < bulletsCnt; ++i)
    {
        unsigned id = msgParts.at(bulletEntrySize * i + idIndex).toInt();
        unsigned posX =  msgParts.at(bulletEntrySize * i + posXIndex).toInt();
        unsigned posY = msgParts.at(bulletEntrySize * i + posYIndex).toInt();
        DynamicObject::CardinalDirection direction = parseDirection(msgParts.at(bulletEntrySize * i + directionIndex));
        unsigned progress = msgParts.at(bulletEntrySize * i + progressIndex).toInt();
        //BulletPtr curr(new Bullet(id, posX, posY, direction, progress));
        //bullets.push_back(curr);
    }
    return bullets;
}
